I tried WafCharm notification and reporting functions using CloudFormation
WafCharm is AWS WAF automation service
Benifits:
●Stronger Defense Create and set rules that are optimal for the user's environment.
●Automation by AI Complete automation of WAF operations from applying rules to handling new vulnerabilities.
●Extensive Support System Reliable support system in case of false-positives. 24/7 technical support (excluding entry plans)
Hands on:
CloudFormationTemplate for Deploying AWS Resources:
AWSTemplateFormatVersion: 2010-09-09 Description: WafCharm production machine Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "Kinesis Data Firehose Configuration" Parameters: - BufferSize - BufferInterval - CompressionFormat Parameters: SystemPrefix: Description: "System prefix of each resource names." Type: String Default: "xxx" ExportServerStackName: Description: the name of Server Stack Type: String Default: xxx-server-stack StreamName: Type: "String" # AllowedPattern: "aws-waf-logs-[A-Za-z0-9]+" Default: "aws-waf-logs-xxx-wafcharm" BufferSize: Type: String Default: '5' BufferInterval: Type: String Default: '60' CompressionFormat: Type: String AllowedValues: [GZIP,HADOOP_SNAPPY,Snappy,UNCOMPRESSED,ZIP] Default: 'GZIP' Resources: # -------- # WAF # -------- WebACL: Type: AWS::WAFv2::WebACL Properties: Name: !Sub '${SystemPrefix}-wafcharm' DefaultAction: Allow: {} Description: AWS WAFv2 WebACL Scope: REGIONAL VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub '${SystemPrefix}' SampledRequestsEnabled: true WAFAssociation: Type: AWS::WAFv2::WebACLAssociation Properties: WebACLArn: !GetAtt WebACL.Arn ResourceArn : Fn::ImportValue: !Sub ${ExportServerStackName}-LoadBalancerArn S3Bucket: # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html Type: "AWS::S3::Bucket" DependsOn: "WAFAssociation" DeletionPolicy: "Delete" Properties: BucketName: !Sub 'aws-waf-logs-${AWS::AccountId}' LifecycleConfiguration: Rules: - ExpirationInDays: "365" Id: s3-lifecycle-rule NoncurrentVersionExpirationInDays: "365" Status: "Enabled" IAMRoleForKinesis: Type: "AWS::IAM::Role" DependsOn: "S3Bucket" DeletionPolicy: "Delete" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: "firehose.amazonaws.com" Action: "sts:AssumeRole" Condition: StringEquals: sts:ExternalId: !Ref "AWS::AccountId" MaxSessionDuration: 3600 Path: "/role/" Policies: - PolicyName: !Sub policy-kinesis-${StreamName} PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: "glue:GetTableVersions" Resource: "*" - Effect: "Allow" Action: - "s3:AbortMultipartUpload" - "s3:GetBucketLocation" - "s3:GetObject" - "s3:ListBucket" - "s3:ListBucketMultipartUploads" - "s3:PutObject" Resource: - !Sub arn:aws:s3:::aws-waf-logs-${AWS::AccountId} - !Sub arn:aws:s3:::aws-waf-logs-${AWS::AccountId}/* - Effect: "Allow" Action: "logs:PutLogEvents" Resource: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/kinesisfirehose/${StreamName}:* - Effect: "Allow" Action: - "kinesis:DescribeStream" - "kinesis:GetShardIterator" - "kinesis:GetRecords" Resource: !Sub arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/${StreamName} RoleName: FirehoseRoleForWafCharm KinesisFirehose: # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-kinesisfirehose-deliverystream.html Type: "AWS::KinesisFirehose::DeliveryStream" DeletionPolicy: "Delete" Properties: DeliveryStreamName: !Ref StreamName DeliveryStreamType: "DirectPut" S3DestinationConfiguration: BucketARN: !Sub arn:aws:s3:::aws-waf-logs-${AWS::AccountId} BufferingHints: IntervalInSeconds: !Ref BufferInterval SizeInMBs: !Ref BufferSize CloudWatchLoggingOptions: Enabled: True LogGroupName: !Sub /aws/kinesisfirehose/${StreamName} LogStreamName: !Ref StreamName CompressionFormat: !Ref CompressionFormat # ErrorOutputPrefix: !Sub error/${StreamName}/ Prefix: !Sub ${StreamName}/ RoleARN: !GetAtt IAMRoleForKinesis.Arn Tags: - Key: Name Value: xxx-firehose-waf-logs IAMRoleForLambda: Type: "AWS::IAM::Role" DependsOn: "KinesisFirehose" DeletionPolicy: "Delete" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: "lambda.amazonaws.com" Action: "sts:AssumeRole" ManagedPolicyArns: - "arn:aws:iam::aws:policy/AWSLambdaExecute" MaxSessionDuration: 3600 Path: "/role/" # PermissionsBoundary: String Policies: - PolicyName: "wafcharm-waflog-s3-read" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "s3:GetObject" Resource: - !Sub arn:aws:s3:::aws-waf-logs-${AWS::AccountId}/* - PolicyName: "wafcharm-waf-log-s3-put" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "s3:*" Resource: - "arn:aws:s3:::wafcharm.com/" - "arn:aws:s3:::wafcharm.com/*" RoleName: "LambdaRoleForWafCharm" Lambda: # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html Type: "AWS::Lambda::Function" DeletionPolicy: "Delete" Properties: Code: ZipFile: | 'use strict'; const toBucket = process.env.WAFCHARM_BUCKET || 'wafcharm.com'; const toPath = process.env.WAFCHARM_PATH || 'waflog/acceptance/v2'; const acl = 'bucket-owner-full-control'; const AWS = require('aws-sdk'); const s3 = new AWS.S3({ apiVersion: '2006-03-01', }); exports.handler = (event) => { let fromParams = { Bucket: event.Records[0].s3.bucket.name, Key: event.Records[0].s3.object.key }; let match = event.Records[0].s3.object.key.match(/\d{4}\/\d{2}\/\d{2}\/\d{2}\/[^\/]*$/); if (!match) { console.error('Not match the AWS WAF full log event. : ', event.Records[0].s3.object.key); return; } s3.getObject(fromParams, (err, data) => { if (err) { console.error('Cannot get object.'); console.error(err); return; } let toParams = { Bucket: toBucket, Key: [toPath, match[0]].join('/'), ACL: acl, Body: data.Body }; send(); function send() { s3.putObject(toParams, (err, data) => { if (err) { console.error('Cannot put object.'); console.error(err); send(); } else { return; } }); } }); }; Description: "For WafCharm" FunctionName: "xxx-lambda-wafcharm" Handler: "index.handler" MemorySize: 128 Role: !GetAtt IAMRoleForLambda.Arn Runtime: "nodejs14.x" Tags: - Key: name Value: xxx-lambda-wafcharm Timeout: 60 WAFCharmLoggingConfiguration: Type: AWS::WAFv2::LoggingConfiguration Properties: LogDestinationConfigs: - !GetAtt KinesisFirehose.Arn ResourceArn: !GetAtt WebACL.Arn LambdaPermission: # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-permission.html Type: "AWS::Lambda::Permission" DeletionPolicy: "Delete" Properties: Action: "lambda:InvokeFunction" # EventSourceToken: String FunctionName: !GetAtt Lambda.Arn Principal: "s3.amazonaws.com" SourceAccount: !Ref "AWS::AccountId" SourceArn: !GetAtt S3Bucket.Arn
Resources Created using Console
Lambda build (trigger)
Iam User
https://www.wafcharm.com/en/blog/aws-iam-setting-for-wafcharm/ 1. open IAM from Services
- click on add user
-
Write user name e.g. WAFCharm and check Access key - Programmatic access
-
Click next permissions
-
Attach existing policy directly:
-
AWSWafFullAccess
-
S3ObjectReadOnly
-
you can skip tags
-
Review and Create User
WAFCharm Reports and email example
https://dev.classmethod.jp/articles/wafcharm-freetrial/